#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
智能项目克隆器入口
负责项目类型分析并委托到具体平台的克隆实现
"""

import os
import re
from typing import Dict, List, Optional

from core.builtin_config_module.builtin_config import BuiltInConfig
from core.resources_module.file_fingerprint import FileFingerprintModifier
from core.cloners_module.android_cloner import AndroidProjectCloner
from core.cloners_module.generic_cloner import GenericProjectCloner
from core.cloners_module.ios_cloner import IosProjectCloner
from core.utils_module.logger import ObfuscationLogger


class ProjectCloner:
    """项目克隆协调器"""

    def __init__(self, config: BuiltInConfig, logger: ObfuscationLogger):
        self.config = config
        self.logger = logger
        fingerprint_modifier = FileFingerprintModifier(config, logger)

        self._ios_cloner = IosProjectCloner(config, logger, fingerprint_modifier)
        self._android_cloner = AndroidProjectCloner(config, logger, fingerprint_modifier)
        self._generic_cloner = GenericProjectCloner(config, logger, fingerprint_modifier)

    def clone_project(
        self,
        source_path: str,
        target_path: str,
        new_bundle_id: Optional[str] = None,
        new_package_name: Optional[str] = None,
        new_project_name: Optional[str] = None,
        modify_fingerprints: bool = True,
    ) -> str:
        """
        克隆项目
        :param source_path: 源项目路径
        :param target_path: 目标项目路径
        :param new_bundle_id: 新的iOS Bundle ID（可选）
        :param new_package_name: 新的Android包名（可选）
        :param new_project_name: 新的项目名称（可选）
        :param modify_fingerprints: 是否修改文件指纹
        :return: 目标项目路径
        :raises FileNotFoundError: 如果源项目不存在
        :raises FileExistsError: 如果目标路径已存在
        """
        if not os.path.exists(source_path):
            raise FileNotFoundError(f"源项目不存在: {source_path}")

        if os.path.exists(target_path):
            raise FileExistsError(f"目标路径已存在: {target_path}")

        self.logger.log_operation("开始克隆项目", f"源: {source_path} -> 目标: {target_path}")

        project_info = self._analyze_project(source_path)
        project_type = project_info["type"]
        self.logger.log_operation("项目分析", f"类型: {project_type}")

        if project_type == "ios":
            return self._ios_cloner.clone(
                source_path=source_path,
                target_path=target_path,
                project_info=project_info,
                new_bundle_id=new_bundle_id,
                new_project_name=new_project_name,
                modify_fingerprints=modify_fingerprints,
            )

        if project_type == "android":
            return self._android_cloner.clone(
                source_path=source_path,
                target_path=target_path,
                project_info=project_info,
                new_package_name=new_package_name,
                new_project_name=new_project_name,
                modify_fingerprints=modify_fingerprints,
            )

        return self._generic_cloner.clone(
            source_path=source_path,
            target_path=target_path,
            modify_fingerprints=modify_fingerprints,
        )

    def _analyze_project(self, project_path: str) -> Dict:
        """
        分析项目类型和结构
        :param project_path: 项目路径
        :return: 项目信息字典
        """
        project_info = {
            "type": "unknown",
            "structure": {},
            "dependencies": [],
            "build_configs": [],
            "root": project_path,
        }

        ios_indicators = {
            "workspace": None,
            "xcodeproj": None,
            "podfile": None,
            "cartfile": None,
            "info_plist": [],
        }

        android_indicators = {
            "build_gradle": [],
            "settings_gradle": None,
            "gradle_properties": None,
            "manifest": [],
        }

        for item in os.listdir(project_path):
            item_path = os.path.join(project_path, item)
            if item.endswith(".xcworkspace"):
                ios_indicators["workspace"] = item_path
            elif item.endswith(".xcodeproj") and "Pods" not in item:
                ios_indicators["xcodeproj"] = item_path
            elif item == "Podfile":
                ios_indicators["podfile"] = item_path
            elif item in ["build.gradle", "build.gradle.kts"]:
                android_indicators["build_gradle"].append(item_path)
            elif item in ["settings.gradle", "settings.gradle.kts"]:
                android_indicators["settings_gradle"] = item_path

        for root, dirs, files in os.walk(project_path):
            dirs[:] = [
                d
                for d in dirs
                if d not in ["Pods", "Carthage", "build", "DerivedData", "node_modules"]
            ]

            for file in files:
                file_path = os.path.join(root, file)
                if file == "Cartfile":
                    ios_indicators["cartfile"] = file_path
                elif file == "Info.plist":
                    ios_indicators["info_plist"].append(file_path)
                elif file in ["build.gradle", "build.gradle.kts"]:
                    # 收集所有 build.gradle 文件（包括子目录中的）
                    android_indicators["build_gradle"].append(file_path)
                elif file == "gradle.properties":
                    android_indicators["gradle_properties"] = file_path
                elif file == "AndroidManifest.xml":
                    android_indicators["manifest"].append(file_path)

        if ios_indicators["workspace"] or ios_indicators["xcodeproj"]:
            project_info["type"] = "ios"
            project_info["structure"] = ios_indicators
            if ios_indicators["podfile"]:
                project_info["dependencies"].extend(
                    self._parse_podfile(ios_indicators["podfile"])
                )
        elif android_indicators["build_gradle"] or android_indicators["manifest"]:
            project_info["type"] = "android"
            project_info["structure"] = android_indicators
            for gradle_file in android_indicators["build_gradle"]:
                project_info["dependencies"].extend(
                    self._parse_gradle_dependencies(gradle_file)
                )

        return project_info

    def _parse_podfile(self, podfile_path: str) -> List[str]:
        """
        解析Podfile以提取依赖
        :param podfile_path: Podfile路径
        :return: 依赖列表
        """
        dependencies: List[str] = []

        try:
            with open(podfile_path, "r", encoding="utf-8") as handle:
                content = handle.read()

            cleaned_lines = []
            for line in content.splitlines():
                stripped = line.strip()
                if stripped.startswith("#"):
                    continue
                if "#" in line:
                    line = line.split("#", 1)[0]
                cleaned_lines.append(line)

            cleaned = "\n".join(cleaned_lines)

            pod_pattern = re.compile(
                r"pod\s+['\"]([^'\"]+)['\"]\s*(?:,(.*?))?(?:\n|$)",
                re.IGNORECASE,
            )

            for match in pod_pattern.finditer(cleaned):
                pod_name = match.group(1).strip()
                options = (match.group(2) or "").strip()
                if options:
                    normalized = re.sub(r"\s+", " ", options)
                    dependencies.append(f"{pod_name} ({normalized})")
                else:
                    dependencies.append(pod_name)

        except Exception as exc:  # noqa: BLE001
            self.logger.log_warning(f"解析Podfile失败 {podfile_path}: {exc}")

        return dependencies

    def _parse_gradle_dependencies(self, gradle_path: str) -> List[str]:
        """
        解析Gradle文件以提取依赖
        :param gradle_path: Gradle文件路径
        :return: 依赖列表
        """
        dependencies: List[str] = []

        try:
            with open(gradle_path, "r", encoding="utf-8") as handle:
                content = handle.read()

            content = re.sub(r"/\*.*?\*/", "", content, flags=re.DOTALL)
            content = re.sub(r"//.*", "", content)

            keywords = (
                "implementation",
                "api",
                "compileOnly",
                "runtimeOnly",
                "testImplementation",
                "androidTestImplementation",
                "debugImplementation",
                "classpath",
                "kapt",
                "annotationProcessor",
            )
            keyword_pattern = "|".join(re.escape(k) for k in keywords)

            groovy_pattern = re.compile(
                rf"(?:{keyword_pattern})\s+\(?\s*[\"'](.*?)[\"']\s*\)?",
                re.IGNORECASE,
            )
            project_pattern = re.compile(
                rf"(?:{keyword_pattern})\s+\(?\s*project\((.*?)\)\s*\)?",
                re.IGNORECASE,
            )
            kotlin_pattern = re.compile(
                rf"(?:{keyword_pattern})\s*\(\s*\"(.*?)\"\s*\)",
                re.IGNORECASE,
            )

            for pattern in (groovy_pattern, kotlin_pattern):
                for match in pattern.finditer(content):
                    dep = match.group(1).strip()
                    if dep and dep not in dependencies:
                        dependencies.append(dep)

            for match in project_pattern.finditer(content):
                declaration = match.group(1).strip()
                if declaration and declaration not in dependencies:
                    dependencies.append(f"project({declaration})")

        except Exception as exc:  # noqa: BLE001
            self.logger.log_warning(f"解析Gradle依赖失败 {gradle_path}: {exc}")

        return dependencies
